/* -----------------
* brief
* 1. this is code gen by tools
* 2. array like [a,b,c] after each field, define the ui html tag in view,
		a for title,
		b for show or not ,
		c for element html tag , currently suppor : check、radio、select、input、textarea、upload tag
* 3. XXX
*/

package wechat

import (
	"errors"
	"fmt"
	mchcore "github.com/chanxuehong/wechat/mch/core"
	"github.com/chanxuehong/wechat/mchv3/corev3"
	"github.com/chanxuehong/wechat/mp/core"
	"github.com/chanxuehong/wechat/mp/jssdk"
	wechatutil "github.com/chanxuehong/wechat/util"
	"github.com/mjiulee/lego"
	"saas_sys_base/common"
	configService "saas_sys_base/modules/config/service"
	"saas_sys_base/redishelper"
	"saas_sys_base/sysconfig"
)

/* table: "tb_wechat"
 * -----------------
 */
type WechatSdk struct {
	appConfigService configService.AppConfigService
}
type WechatSdkClient struct {
	appConfigService configService.AppConfigService
}

/*
有两种客户端，一种是weapp客户端，一种是mp（公众号客户端）
但在底层存储时，只以appid区分
上层取时，需要区分是取对应miniApp内的weapp还是mp
*/
//方案二：采用appid为key，获取client
var _wxCoreClientMap map[string]*WxCoreCli

//var _wxCheckConfigMap map[string]*common.AppConfig //用于检测是否有重复的公众号配置
/*
TODO 重构WxCoreCli这个结构的思路：
1. 将此结构分为3个子结构
2. 分为MP client、weapp client、mch client，然后使用同一个抽象接口提供服务
3. MP client和jssdk client可以放在一个结构里面，因为jssdk的appid就是用的mp的appid
*/
type WxCoreCli struct {
	wxCli             *core.Client       //MP或weapp类型时有效
	jssdkCli          *jssdk.JssdkClient //jssdk类型时有效，TODO 这里core.Client和jssdk.JssdkClient要分开为两个client，以后重构的时候要考虑一下
	wxMchCli          *mchcore.Client    //mch类型时有效
	wxMchV3Cli          *corev3.ClientV3    //mchv3类型时有效
	cliType           WxClientType
	appconfigforcheck *common.AppConfig //这个只用于检测配置重复，不可取出来作为其它用途,并且在这里只有wechat部分的配置有意义
}

func init() {
	//系统级相关配置
	if sysconfig.Config.Debug.IsDebugMode {
		wechatutil.SetDevMode(true)
	}

	_wxCoreClientMap = make(map[string]*WxCoreCli)
	//_wxCheckConfigMap = make(map[string]*common.AppConfig)

	t := new(WechatSdkClient)
	appConfigList := t.appConfigService.GetAllAppConfig()

	/*取消，这里不加载sysconfig   sysConfig := t.appConfigService.GetSysConfig() //这里补一脚，把sys的配置加到里面，一起把sys配置对应的wx server也起起来
	appConfigList = append(appConfigList, sysConfig)*/

	for _, vConfig := range appConfigList {
		//wechatConfig := v.App_Config_Data.Wechat
		/*init 函数里面不能查询数据库，所以删除 vConfig, err := t.appConfigService.GetConfigByWho(*common.NewWho(vConfig.Who.Tenant_Id, vConfig.Who.Mini_App_Id, vConfig.Who.Uid))
		if err != nil {
			panic(err)
		}*/

		if vConfig.Parent_Mini_App_Id != 0 {
			continue //子app配置中的微信配置不起作用，以主app微信配置为主
		}
		fmt.Printf("loading config <tid:%d, appid:%d, uid%d>\n", vConfig.Who.Tenant_Id, vConfig.Who.Mini_App_Id, vConfig.Who.Uid)
		MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_MP) //会创建MP和JS SDK 两个client
		MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_WEAPP)
		//MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_JSSDK)创建MP时就已经一起创建JSSDK CLIENT了，所以这里不需要

		//DONE 后面还有在MakeWxClientToMap()里处理merchat类型的逻辑
		MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_MP_MCH)
		MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_WEAPP_MCH)

		//MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_MP_MCHv3) 暂时不支持mp+mchv3
		MakeWxClientToMap(vConfig, WX_CLIENT_TYPE_WEAPP_MCHv3)

	}

}

//new小程序client
func NewWeappClient(wxAppId string, wxAppSecret string) *core.Client {
	if wxAppId == "" || wxAppSecret == "" {
		panic("wxAppId or wxAppSecret is not set")
		return nil
	}

    AccessTokenServer := core.NewRedisAccessTokenServer(wxAppId, wxAppSecret, nil, redishelper.GetRedisConn) //支持分布式token
	//AccessTokenServer := core.NewDefaultAccessTokenServer(wxAppId, wxAppSecret, nil)
	WechatClient := core.NewClient(AccessTokenServer, nil)
	return WechatClient
}

//new公众号client，其中包括公众号client和jssdk client
func NewWxMpClient(wxAppId string, wxAppSecret string) (*core.Client, *jssdk.JssdkClient) {
	if wxAppId == "" || wxAppSecret == "" {
		panic("wxAppId or wxAppSecret is not set")
		return nil, nil
	}

	AccessTokenServer := core.NewRedisAccessTokenServer(wxAppId, wxAppSecret, nil, redishelper.GetRedisConn) //支持分布式token
	//删除使用redis分布式token AccessTokenServer := core.NewDefaultAccessTokenServer(wxAppId, wxAppSecret, nil)
	WechatClient := core.NewClient(AccessTokenServer, nil)
	cardTicketSvr := jssdk.NewDefaultCardTicketServer(WechatClient)
	ticketSvr := jssdk.NewDefaultTicketServer(WechatClient)
	jssdkClt := jssdk.NewTicketClient(ticketSvr, cardTicketSvr)
	return WechatClient, jssdkClt
}

func NewWxMchClient(wxAppId string, mchId, apiKey string, apiClientCert, apiClientKey string) *mchcore.Client {
	if wxAppId == "" || mchId == "" || apiKey == "" {
		panic("wxAppId or mchId or apiKey is not set")
		return nil
	}

	httpClt, _ := mchcore.NewTLSHttpClient2([]byte(apiClientCert), []byte(apiClientKey))
	WechatClient := mchcore.NewClient(wxAppId, mchId, apiKey, httpClt)
	return WechatClient
}

func NewWxMchV3Client(wxAppId, mchId, serialNo, apiV3Key string, mchPriKeyText string) *corev3.ClientV3 {
	if wxAppId == "" || mchId == "" || apiV3Key == "" {
		panic("wxAppId or mchId or apiV3Key is not set")
		return nil
	}

	WechatClient := corev3.NewClientV3(wxAppId, mchId, apiV3Key, serialNo, mchPriKeyText, nil)
	return WechatClient
}

// 获取公众号api client
func (t *WechatSdkClient) GetMpClientByWho(who common.Who) (*core.Client, error) {
	wxCoreCli := t.getWxCoreCliByWho(who, WX_CLIENT_TYPE_MP)
	if wxCoreCli == nil {
		return nil, errors.New("app未绑定公众号")
	}

	if wxCoreCli.wxCli == nil {
		return nil, errors.New("app未绑定公众号")
	}
	return wxCoreCli.wxCli, nil
}

// 获取小程序api client
func (t *WechatSdkClient) GetWeappClientByWho(who common.Who) (*core.Client, error) {
	wxCoreCli := t.getWxCoreCliByWho(who, WX_CLIENT_TYPE_WEAPP)
	if wxCoreCli == nil {
		return nil, errors.New("app未绑定小程序")
	}

	if wxCoreCli.wxCli == nil {
		return nil, errors.New("app未绑定小程序")
	}
	return wxCoreCli.wxCli, nil
}

func (t *WechatSdkClient) GetJssdkClientByWho(who common.Who) (*jssdk.JssdkClient, error) {
	wxCoreCli := t.getWxCoreCliByWho(who, WX_CLIENT_TYPE_WEAPP)
	if wxCoreCli == nil {
		return nil, errors.New("app未绑定公众号")
	}

	if wxCoreCli.jssdkCli == nil {
		return nil, errors.New("app未绑定公众号")
	}
	return wxCoreCli.jssdkCli, nil
}

// 获取商户支付api client
func (t *WechatSdkClient) GetWeappMchClientByWho(who common.Who) (*mchcore.Client, error) {
	wxCoreCli := t.getWxCoreCliByWho(who, WX_CLIENT_TYPE_WEAPP_MCH)
	if wxCoreCli == nil {
		return nil, errors.New("app未绑定商户号")
	}

	if wxCoreCli.wxMchCli == nil {
		return nil, errors.New("app未绑定商户号")
	}
	return wxCoreCli.wxMchCli, nil
}

// 获取商户支付api client
func (t *WechatSdkClient) GetMpMchClientByWho(who common.Who) (*mchcore.Client, error) {
	wxCoreCli := t.getWxCoreCliByWho(who, WX_CLIENT_TYPE_MP_MCH)
	if wxCoreCli == nil {
		return nil, errors.New("app未绑定商户号")
	}

	if wxCoreCli.wxMchCli == nil {
		return nil, errors.New("app未绑定商户号")
	}
	return wxCoreCli.wxMchCli, nil
}

func (t *WechatSdkClient) GetMchClientByWhoWrapper(who *common.Who, clientType WxClientType) (*mchcore.Client, error) {
	if clientType != WX_CLIENT_TYPE_WEAPP_MCH && clientType != WX_CLIENT_TYPE_MP_MCH {
		panic(fmt.Sprintf("GetMchClientByWhoWrapper not support clientType(%v)", clientType))
	}

	if clientType == WX_CLIENT_TYPE_WEAPP_MCH {
		clt, err := t.GetWeappMchClientByWho(*who)
		return clt, err
	} else if clientType == WX_CLIENT_TYPE_MP_MCH {
		clt, err := t.GetMpMchClientByWho(*who)
		return clt, err
	}
    return nil, fmt.Errorf("GetMchClientByWhoWrapper not support clientType(%v)", clientType)
}
//
func (t *WechatSdkClient) GetMpMchv3ClientByWho(who *common.Who) (*corev3.ClientV3, error) {
	wxCoreCli := t.getWxCoreCliByWho(*who, WX_CLIENT_TYPE_MP_MCHv3)
	if wxCoreCli == nil {
		return nil, errors.New("mini app未绑定商户号v3")
	}

	if wxCoreCli.wxCli == nil {
		return nil, errors.New("mini app未绑定商户号v3")
	}
	return wxCoreCli.wxMchV3Cli, nil
}

func (t *WechatSdkClient) GetWeappMchv3ClientByWho(who *common.Who) (*corev3.ClientV3, error) {
	wxCoreCli := t.getWxCoreCliByWho(*who, WX_CLIENT_TYPE_WEAPP_MCHv3)
	if wxCoreCli == nil {
		return nil, errors.New("mini app未绑定商户号v3")
	}

	if wxCoreCli.wxMchV3Cli == nil {
		return nil, errors.New("mini app未绑定商户号v3")
	}
	return wxCoreCli.wxMchV3Cli, nil
}

type WxClientType uint

const (
	WX_CLIENT_TYPE_MP    WxClientType = 0 //公众号
	WX_CLIENT_TYPE_JSSDK WxClientType = 1 //微信jssdk
	WX_CLIENT_TYPE_WEAPP WxClientType = 2 //微信小程序

	WX_CLIENT_TYPE_MP_MCH    WxClientType = 3 //公众号关联的商户号
	WX_CLIENT_TYPE_WEAPP_MCH WxClientType = 4 //微信小程序关联的商户号
	WX_CLIENT_TYPE_MP_MCHv3    WxClientType = 5 //mp mch v3
	WX_CLIENT_TYPE_WEAPP_MCHv3    WxClientType = 6 //weapp mch v3

)

/*

 */
func (t *WechatSdkClient) getWxCoreCliByWho(who common.Who, clientType WxClientType) *WxCoreCli {
	keyOfMap, err := t.getKeyOfWxCoreCli(who, clientType)
	if err != nil {
		return nil
	}

	wxCoreCli, ok := _wxCoreClientMap[keyOfMap]
	if !ok {
		return nil
	}

	return wxCoreCli
}

func (t *WechatSdkClient) GetWxClientByWeappid(weappid string) *core.Client {
	wxCoreCli, ok := _wxCoreClientMap[weappid]
	if !ok {
		return nil
	}
	return wxCoreCli.wxCli
}

func (t *WechatSdkClient) getKeyOfWxCoreCli(who common.Who, clientType WxClientType) (key string, err error) {
	var keyOfMap string
	config, err := t.appConfigService.GetConfigByWho(who)
	if err != nil {
		panic("panic: not found appconfig")
		return "", err
	}

	wechatConfig := config.App_Config_Data.Wechat
	var appid string
	var mchid string

	switch clientType {
	case WX_CLIENT_TYPE_MP, WX_CLIENT_TYPE_JSSDK:
		appid = wechatConfig.WX_MP_APPID
		keyOfMap = appid
	case WX_CLIENT_TYPE_WEAPP:
		appid = wechatConfig.WX_APPID
		keyOfMap = appid
	case WX_CLIENT_TYPE_MP_MCH,WX_CLIENT_TYPE_MP_MCHv3:
		appid = wechatConfig.WX_MP_APPID
		mchid = wechatConfig.WX_MERCHANT_ID
		keyOfMap = KeyOfMch(appid, mchid, clientType)
	case WX_CLIENT_TYPE_WEAPP_MCH, WX_CLIENT_TYPE_WEAPP_MCHv3:
		appid = wechatConfig.WX_APPID
		mchid = wechatConfig.WX_MERCHANT_ID
		keyOfMap = KeyOfMch(appid, mchid, clientType)
	default:
		msg := fmt.Sprintf("panic: not support wx client type <%d>", clientType)
		panic(msg)
		return "", fmt.Errorf(msg)
	}

	return keyOfMap, nil
}

func KeyOfMch(appid string, mchid string, clientType WxClientType) string {
	key := fmt.Sprintf("%s_%s", appid, mchid)
	if clientType == WX_CLIENT_TYPE_MP_MCHv3 || clientType == WX_CLIENT_TYPE_WEAPP_MCHv3 {
		key = key + "_v3"
	}
	return key
}

func MakeWxClientToMap(appconfig *common.AppConfig, clientType WxClientType) error {
	wechatConfig := appconfig.App_Config_Data.Wechat
	var keyOfMap string
	var appid string
	var appsecret string
	var mchid string
	var paykey string
	if clientType == WX_CLIENT_TYPE_MP || clientType == WX_CLIENT_TYPE_JSSDK {
		appid = wechatConfig.WX_MP_APPID
		appsecret = wechatConfig.WX_MP_APPSECRET
		keyOfMap = appid
	} else if clientType == WX_CLIENT_TYPE_WEAPP {
		appid = wechatConfig.WX_APPID
		appsecret = wechatConfig.WX_APPSECRET
		keyOfMap = appid
	} else if clientType == WX_CLIENT_TYPE_MP_MCH || clientType == WX_CLIENT_TYPE_MP_MCHv3  {
		appid = wechatConfig.WX_MP_APPID //公众号appid
		mchid = wechatConfig.WX_MERCHANT_ID
		paykey = wechatConfig.WX_PAY_KEY
		keyOfMap = KeyOfMch(appid, mchid, clientType)
	} else if clientType == WX_CLIENT_TYPE_WEAPP_MCH || clientType == WX_CLIENT_TYPE_WEAPP_MCHv3 {
		appid = wechatConfig.WX_APPID //小程序appid
		mchid = wechatConfig.WX_MERCHANT_ID
		paykey = wechatConfig.WX_PAY_KEY
		keyOfMap = KeyOfMch(appid, mchid, clientType)
	} else {
		errmsg := fmt.Sprintf("panic: not support wx client type <%d>", clientType)
		panic(errmsg)
		return errors.New(errmsg)
	}

	if appid == "" {
		return nil
	}

	_, ok := _wxCoreClientMap[keyOfMap]
	if ok {
		//如果已经存在，则检测新旧值是否一致，如果不一致则报错
		preWxCoreCli, ok2 := _wxCoreClientMap[keyOfMap] //把之前的值记录下来，用作与新值对比
		if !ok2 {                                       //理论上不可能到这一个分支
			panic("check wxCheckServerMap error")
			return errors.New("MakeWxClientToMap failed")
		}
		v := appconfig
		v2 := preWxCoreCli.appconfigforcheck
		if clientType != preWxCoreCli.cliType {
			panic("type must be the same")
			return errors.New("type must be the same")
		}

		if clientType == WX_CLIENT_TYPE_WEAPP  {
			if clientType != preWxCoreCli.cliType ||
				v.App_Config_Data.Wechat.WX_APPSECRET != v2.App_Config_Data.Wechat.WX_APPSECRET { //检查mch配置是否一致
				panic(fmt.Sprintf("there may be mistake in wechat weapp config, the same weapp config must be same! key<%s>", keyOfMap))
				return errors.New("MakeWxClientToMap failed")
			}
		}

		if clientType == WX_CLIENT_TYPE_MP || clientType == WX_CLIENT_TYPE_JSSDK {
			if clientType != preWxCoreCli.cliType ||
				v.App_Config_Data.Wechat.WX_MP_APPSECRET != v2.App_Config_Data.Wechat.WX_MP_APPSECRET ||
				v.App_Config_Data.Wechat.WX_MP_TOKEN != v2.App_Config_Data.Wechat.WX_MP_TOKEN ||
				v.App_Config_Data.Wechat.WX_MP_ENCODED_AESKey != v2.App_Config_Data.Wechat.WX_MP_ENCODED_AESKey {
				panic(fmt.Sprintf("there may be mistake in wechat mp config, the same mp config must be same! key<%s>", keyOfMap))
				return errors.New("MakeWxClientToMap failed")
			}
		}

		if clientType == WX_CLIENT_TYPE_WEAPP_MCH ||  clientType == WX_CLIENT_TYPE_MP_MCH ||
			clientType == WX_CLIENT_TYPE_WEAPP_MCHv3 ||  clientType == WX_CLIENT_TYPE_MP_MCHv3 {
			//检查mch配置是否一致
			if v.App_Config_Data.Wechat.WX_MERCHANT_ID != v2.App_Config_Data.Wechat.WX_MERCHANT_ID ||
				v.App_Config_Data.Wechat.WX_PAY_KEY != v2.App_Config_Data.Wechat.WX_PAY_KEY {
				panic(fmt.Sprintf("there may be mistake in wechat mch config, the same mch config must be same! key<%s>", keyOfMap))
				return errors.New("MakeWxClientToMap failed")
			}

		}

		if clientType == WX_CLIENT_TYPE_WEAPP_MCHv3 ||  clientType == WX_CLIENT_TYPE_MP_MCHv3 {
			if v.App_Config_Data.Wechat.WX_MCH_V3.WX_PAY_API_V3_KEY != v2.App_Config_Data.Wechat.WX_MCH_V3.WX_PAY_API_V3_KEY ||
				v.App_Config_Data.Wechat.WX_MCH_V3.WX_MCH_V3_CERT_SERIAL_NO != v2.App_Config_Data.Wechat.WX_MCH_V3.WX_MCH_V3_CERT_SERIAL_NO ||
				v.App_Config_Data.Wechat.WX_MCH_V3.WX_MCH_V3_PRI_KEY_CERT != v2.App_Config_Data.Wechat.WX_MCH_V3.WX_MCH_V3_PRI_KEY_CERT {
				panic(fmt.Sprintf("there may be mistake in wechat mch config, the same mch config must be same! key<%s>", keyOfMap))
				return errors.New("MakeWxClientToMap failed")
			}
		}

	} else {
		_wxCoreClientMap[keyOfMap] = new(WxCoreCli)
		if clientType == WX_CLIENT_TYPE_MP_MCH || clientType == WX_CLIENT_TYPE_WEAPP_MCH {
			//mch的key由appid+mchid组成
			_wxCoreClientMap[keyOfMap].wxMchCli = NewWxMchClient(appid, mchid, paykey, wechatConfig.WX_PAYMENT_CERT, wechatConfig.WX_MCH_V3.WX_MCH_V3_PRI_KEY_CERT)
		} else if clientType == WX_CLIENT_TYPE_MP_MCHv3 || clientType == WX_CLIENT_TYPE_WEAPP_MCHv3 {
			if wechatConfig.WX_MCH_V3.WX_PAY_API_V3_KEY == "" ||
				wechatConfig.WX_MCH_V3.WX_MCH_V3_CERT_SERIAL_NO == "" ||
				wechatConfig.WX_MCH_V3.WX_MCH_V3_PRI_KEY_CERT == "" {
				warningstr := fmt.Sprintf("Warn: appconf have no config for mchv3(mini_app_id:%d)", appconfig.Who.Mini_App_Id)
				fmt.Printf(warningstr)
				lego.LogError(warningstr)
				return nil
			}
			_wxCoreClientMap[keyOfMap].wxMchV3Cli = NewWxMchV3Client(appid, mchid,
				                                                     wechatConfig.WX_MCH_V3.WX_MCH_V3_CERT_SERIAL_NO,
				                                                     wechatConfig.WX_MCH_V3.WX_PAY_API_V3_KEY,
				                                                     wechatConfig.WX_MCH_V3.WX_MCH_V3_PRI_KEY_CERT)
		} else {
			if clientType == WX_CLIENT_TYPE_JSSDK || clientType == WX_CLIENT_TYPE_MP {
				wxCli, jssdkCli := NewWxMpClient(appid, appsecret)
				_wxCoreClientMap[keyOfMap].wxCli = wxCli
				_wxCoreClientMap[keyOfMap].jssdkCli = jssdkCli
			} else { // == WX_CLIENT_TYPE_WEAPP
				wxCli := NewWeappClient(appid, appsecret)
				_wxCoreClientMap[keyOfMap].wxCli = wxCli
			}

		}

		_wxCoreClientMap[keyOfMap].cliType = clientType
		_wxCoreClientMap[keyOfMap].appconfigforcheck = appconfig
	}

	return nil
}

/*以下函数弃用，减少一层，减少代码量，减少了维护量

//微信客户端分为mp（公众号）和wapp（小程序）客户端
func (t *WechatSdkClient) GetWxClientByWho(who common.Who, clientType WxClientType) *core.Client {
	if clientType != WX_CLIENT_TYPE_MP && clientType != WX_CLIENT_TYPE_WEAPP {
		return nil
	}
	wxCoreCli := t.getWxCoreCliByWho(who, clientType)
	if wxCoreCli == nil {
		return nil
	}

	return wxCoreCli.wxCli
}

func (t *WechatSdkClient) getWxJssdkClientByWho(who common.Who, clientType WxClientType) *jssdk.JssdkClient {
	if clientType != WX_CLIENT_TYPE_JSSDK {
		return nil
	}
	wxCoreCli := t.getWxCoreCliByWho(who, clientType)
	if wxCoreCli == nil {
		return nil
	}

	return wxCoreCli.jssdkCli
}

//微信客户端分为mp（公众号）和weapp（小程序）客户端
func (t *WechatSdkClient) getWxMchClientByWho(who common.Who, clientType WxClientType) *mchcore.Client {
	if clientType != WX_CLIENT_TYPE_MP_MCH && clientType != WX_CLIENT_TYPE_WEAPP_MCH {
		return nil
	}
	wxCoreCli := t.getWxCoreCliByWho(who, clientType)
	if wxCoreCli == nil {
		return nil
	}

	return wxCoreCli.wxMchCli
}
*/

